package cn.com.idmy.cloud.service;

import cn.com.idmy.base.FieldGetter;
import cn.com.idmy.base.model.Page;
import cn.com.idmy.base.util.Assert;
import cn.com.idmy.orm.core.Delete;
import cn.com.idmy.orm.core.Query;
import cn.com.idmy.orm.core.Update;
import lombok.NonNull;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;

import java.math.BigDecimal;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;

public interface DefaultService<T, ID> {
    Logger log();

    @Nullable ID newId();

    default @NotNull Query<T> q() {
        return q(true);
    }

    @NotNull Query<T> q(boolean nullable);

    default @NotNull Query<T> q(@Nullable T params) {
        return q(params, true);
    }

    @NotNull Query<T> q(@Nullable T params, boolean nullable);

    @Nullable T getNullable(@NonNull ID id);

    @Nullable T getNullable(@NotNull Query<T> q);

    @Nullable T getNullable(@NotNull Query<T> q, @NotNull FieldGetter<T, ?> f1, @NotNull FieldGetter<T, ?> f2);

    @Nullable T getNullable(@NotNull Query<T> q, @NotNull FieldGetter<T, ?> f1, @NotNull FieldGetter<T, ?> f2, @NotNull FieldGetter<T, ?> f3);

    @NotNull T get(@NotNull Query<T> q, @NotNull FieldGetter<T, ?> f1, @NotNull FieldGetter<T, ?> f2);

    @NotNull T get(@NotNull Query<T> q, @NotNull FieldGetter<T, ?> f1, @NotNull FieldGetter<T, ?> f2, @NotNull FieldGetter<T, ?> f3);

    @NotNull T get(@NonNull ID id);

    @NotNull T get(@NotNull Query<T> q);

    @NotNull T get(@NotNull Query<T> q, @NotNull String msg, @NotNull Object... params);

    <R> @Nullable R getNullable(@NotNull FieldGetter<T, R> f, @NonNull ID id);

    <R> @Nullable R getNullable(@NotNull FieldGetter<T, R> f, @NotNull Query<T> q);

    <R> @NotNull R get(@NotNull FieldGetter<T, R> f, @NonNull ID id);

    <R> @NotNull R get(@NotNull FieldGetter<T, R> f, @NonNull ID id, @NotNull String msg, @NotNull Object... params);

    <R> @NotNull R get(@NotNull FieldGetter<T, R> f, @NotNull Query<T> q);

    <R> @NotNull R get(@NotNull FieldGetter<T, R> f, @NotNull Query<T> q, @NotNull String msg, @NotNull Object... params);

    @NotNull List<T> all();

    @NotNull List<T> list(@Nullable Collection<ID> ids);

    @NotNull List<T> list(@NotNull Collection<ID> ids, @NotNull String msg, @NotNull Object... params);

    @NotNull <R> List<R> list(@NotNull FieldGetter<T, R> f, @Nullable Collection<ID> ids);

    @NotNull List<T> list(@NotNull Query<T> q);

    @NotNull
    default List<T> list(@NotNull Query<T> q, @NotNull String msg, @NotNull Object... params) {
        return Assert.notEmpty(list(q), msg, params);
    }

    @NotNull <R> List<R> list(@NotNull FieldGetter<T, R> f, @NotNull Query<T> q);

    @NotNull List<T> list(@NotNull Query<T> q, @NotNull FieldGetter<T, ?> f1, @NotNull FieldGetter<T, ?> f2);

    @NotNull List<T> list(@NotNull Query<T> q, @NotNull FieldGetter<T, ?> f1, @NotNull FieldGetter<T, ?> f2, @NotNull FieldGetter<T, ?> f3);

    @NotNull Map<ID, T> map(@Nullable ID[] ids);

    @NotNull Map<ID, T> map(@Nullable Collection<ID> ids);

    @NotNull <R> Map<R, T> map(@NotNull FieldGetter<T, R> key, @NotNull Query<T> q);

    @NotNull <K, V> Map<K, V> map(@NotNull FieldGetter<T, K> key, @NotNull FieldGetter<T, V> val, @NotNull Query<T> q);

    @NotNull <K, V> Map<K, List<V>> maps(@NotNull FieldGetter<T, K> key, @NotNull FieldGetter<T, V> val, @NotNull Query<T> q);

    default @NotNull Page<T> page(@NotNull Page<?> in) {
        return page(in, q());
    }

    @NotNull Page<T> page(@NotNull Page<?> in, @NotNull Query<T> q);

    boolean has(@NotNull Query<T> q);

    void has(@NotNull Query<T> q, @NotNull String msg, @NotNull Object... params);

    boolean notHas(@NotNull Query<T> q);

    void notHas(@NotNull Query<T> q, @NotNull String msg, @NotNull Object... params);

    boolean has(@NonNull ID id);

    void has(@NonNull ID id, @NotNull String msg, @NotNull Object... params);

    boolean notHas(@NonNull ID id);

    void notHas(@NonNull ID id, @NotNull String msg, @NotNull Object... params);

    long count(@NotNull Query<T> q);

    <R extends Number> @Nullable R abs(@NotNull FieldGetter<T, R> f, @NotNull Query<T> q);

    <R extends Number> @Nullable R avg(@NotNull FieldGetter<T, R> f, @NotNull Query<T> q);

    <R extends Number> @Nullable R max(@NotNull FieldGetter<T, R> f, @NotNull Query<T> q);

    <R extends Number> @Nullable R min(@NotNull FieldGetter<T, R> f, @NotNull Query<T> q);

    @NotNull BigDecimal sum(@NotNull FieldGetter<T, BigDecimal> f, @NotNull Query<T> q);

    long sumLong(@NotNull FieldGetter<T, Long> f, @NotNull Query<T> q);

    int sumInt(@NotNull FieldGetter<T, Integer> f, @NotNull Query<T> q);

    @Nullable BigDecimal sumNullable(@NotNull FieldGetter<T, BigDecimal> f, @NotNull Query<T> q);

    @Nullable Long sumLongNullable(@NotNull FieldGetter<T, Long> f, @NotNull Query<T> q);

    @Nullable Integer sumIntNullable(@NotNull FieldGetter<T, Integer> f, @NotNull Query<T> q);

    boolean create(@NonNull T t);

    boolean create(@NonNull Collection<T> ls);

    boolean create(@NonNull Collection<T> ls, int size);

    T copy(@NonNull ID id, @NotNull Consumer<T> consumer);

    boolean update(@Nullable Collection<T> ls);

    boolean update(@Nullable Collection<T> ls, boolean ignoreNull);

    boolean update(@Nullable Collection<T> ls, int size, boolean ignoreNull);

    boolean update(@NonNull T t);

    boolean update(@NonNull T t, boolean ignoreNull);

    boolean update(@NotNull Update<T> u);

    default void update(@NotNull Update<T> u, @NotNull String msg, Object... params) {
        Assert.isTrue(update(u), msg, params);
    }

    boolean createOrUpdate(T t);

    default Update<T> u() {
        return u(false);
    }

    Update<T> u(boolean nullable);

    boolean delete(@NotNull Delete<T> d);

    default void delete(@NotNull Delete<T> d, @NotNull String msg, Object... params) {
        Assert.isTrue(delete(d), msg, params);
    }

    boolean delete(@NonNull ID id);

    boolean delete(@Nullable Collection<ID> ids);

    default Delete<T> d() {
        return d(false);
    }

    Delete<T> d(boolean nullable);
}